Data Mining Versuch Fahrzeugdaten

Abzugeben ist das Jupyter Notebook mit dem verlangten Implementierungen, den entsprechenden Ausgaben, Antworten und Diskussionen/Beschreibungen. Das Notebook ist als .ipynb und als .html abzugeben.

Einführung

Lernziele:

In diesem Versuch sollen Kenntnisse in folgenden Themen vermittelt werden:

Vorbereitung

Datenbankzugriff

  1. Installieren Sie PostgreSQL. Mit PostgreSQL sollte auch pgAdmin installiert werden. PgAdmin ist eine open-source Software für die Entwicklung und die Administration von PostgreSQL Datenbanken.
  2. Legen Sie über pgAdmin eine Datenbank für das Datamining-Praktikum an. In diese Datenbank werden alle in diesem Versuch relevanten Tabellen geschrieben.
  3. Für den Datenbankzugriff aus Python heraus wird in diesem Versuch SQLAlchemy eingesetzt. Machen Sie sich mit den Basics von SQLAlchemy vertraut, z.B. mithilfe von https://gitlab.mi.hdm-stuttgart.de/maucher/DataScienceProgramming/blob/master/Python/Lecture/07DataBasePandas.ipynb, Abschnitt Using SQLAlchemy and Pandas.

Pandas Dataframe

Machen Sie sich mit den Grundlagen von Pandas vertraut.

Machine Learning

Machen Sie sich mit Entscheidungsbäumen, Random Forest, Single Layer Perzeptron und Multi Layer Perzeptron vertraut.

Durchführung

Einlesen der Daten aus .csv und Ablage in PostgreSQL

In diesem ersten Teil des Versuchs sollen die relevanten Daten aus dem .csv-File eingelesen und in einer PostgreSQL-Tabelle abgelegt werden. Das benötigte File Fahrzeuginformationen.csv liegt im aktuellen Verzeichnis.

  1. Laden Sie die .csv-Datei in einen Pandas Dataframe.

  2. Zeigen Sie für den angelegten Dataframe

    • die ersten 10 Zeilen
    • die Größe (Anzahl Zeilen und Anzahl Spalten)
    • die Anzahl der NaNs pro Spalte an.
  3. Zeigen Sie mit der Pandas-Dataframe Methode info(), den Datentyp aller Spalten an. Der Typ der Spalte CO2-Emissionen ist tatsächlich kein numerischer Typ. Finden Sie heraus warum das so ist. Beheben Sie den Fehler und sorgen Sie dafür, dass auch diese Spalte einen numerischen Typ hat.

  4. Schreiben Sie den im vorigen Schritt angepassten Dataframe mit der Pandas Methode to_sql() in eine Datenbanktabelle mit dem Namen vehicledata.

Als Erstes wird die pandas Bibliothek importiert, da diese im Laufe dieses Notebooks des Öfteren verwendet wird.

Im nächsten Schritt werden die relevanten Fahrzeuginformationen aus der CSV Datei geladen, damit sie in den folgenden Zeilen verarbeitet werden können. Dies kann einfach mit der pandas-eigenen Funktion read_csv erreicht werden. Diese liest eine vorhandene CSV-Datei in ein Pandas-Dataframe ein.

Um sicher zu stellen, dass die Daten auch korrekt und vollständig sind, sollte man einen Blick darauf werfen. Die folgenden Punkte sollen betrachtet werden:

Die ersten zehn Zeilen des Dataframes:

Um herauszufinden, ob es nicht-angegebene Werte in den jeweiligen Spalten gibt, wird sich einer dictionary-comprehension bedient, welche die jeweiligen Spalten mit der Anzahl ihrer nicht-angegebenen / fehlenden Werte in ein dictionary schreibt. Mit Hilfe dieses dictionaries ist es ebenso möglich, die Gesamtzahl der nicht-angegebenen Werte anzuzeigen, indem die Summe über alle Werte erzeugt wird. In vorliegendem Beispiel sind alle Datenwerte vorhanden.

Die Datentypen der Spalten bzw. gesamten Zeileneinträge werden sichtbar gemacht. Es fällt auf, dass Die Spalte 'CO2-Emissionen' vom Datentyp object ist statt wie erwartet vom Datentyp int64 oder float64.

Vermutung: Dass die Spalte CO2-Emissionen vom Typ object ist, führt zu der Vermutung, dass es darin einige Werte geben muss, die keine ''reinen Zahlen'' sind, sondern beispielsweise Buchstaben und andere Zeichen enthalten.

Lösung 1: Für die Lösung des Problems wird eine Series erstellt, in der der Datentyp aller Zeileneinträge der Spalte 'CO2-Emissionen', wenn möglich, in einen numerischen Datentyp gewandelt wird. Wenn sich ein Wert nicht umwandeln lässt, wird an seine Stelle stattdessen NaN geschrieben. Die Zeilen des Dataframes, in denen sich (in der Series) ein NaN befindet, werden ausgegeben.

Lösung 2: Im Folgenden werden nacheinander alle in der Spalte CO2-Emissionen befindlichen Werte zu einem float gecastet. Falls dies nicht für alle Werte funktionieren sollte, werden diese jeweils als Error gefangen und angezeigt.

Die Ergebnisse zeigen, dass einige Werte der Spalte CO2-Emissionen mit einem Komma statt einem Punkt abgespeichert sind. Dies könnte daher kommen, dass das Datenset von deutschprachigen Autoren stammt und im Deutschen Dezimalzahlen mit einem Komma statt einem Punkt getrennt werden.

Es wird eine Kopie des Dataframes vehicle_data erstellt, in welcher die Kommas der betroffenen Werte mithilfe der replace()-Methode zu Punkten umgewandelt und hiernach der Datentyp aller Werte in der Spalte CO2-Emissionen in float gewandelt wird.

Die Verbindung zur PostgreSql-Datenbank wird aufgebaut und das neue Dataframe wird in die Datenbanktabelle geschrieben.

Exemplarische Datenbankabfragen

  1. Verwenden Sie Pandas Dataframe Methode read_sql_query() um 3 für Sie interessante Datenbankabfragen zu implementieren. Die Resultate der Abfragen werden in einen Pandas Dataframe geschrieben. Zeigen Sie diese an.

Im Folgenden werden drei unterschiedliche Queries auf die Datenbank angewandt und ausgegeben. Um das Ausfuehren und Verarbeiten von Queries zu vereinfachen, wird sich einer Funktion bedient, die Queries auf die Datenbank anwendet und das Ergebnis auf eine gegebene Funktion anwendet. In diesem Fall ist diese Funktion nur eine simple Ausgabe in die Konsole. Es werden zunaechst die Queries definiert, die ausgefuehrt werden sollen.

Jetzt wird die Funktion fuer die Verarbeitung der Queries definiert. Hier wird sich einiger praktischer idiomatischer Python-Features, wie List-Comprehensions und Funktionales Programmieren, bedient.

Jetzt wird diese Funktion auf die gegebenen Queries angewandt.

Data Exploration

  1. Zeigen Sie für alle Spalten die Anzahl der unterschiedlichen Werte in dieser Spalte an.
  2. Benutzen Sie die Pandas Dataframe Methode describe() um sämtliche deskriptiven Statistiken anzuzeigen.
  3. Legen Sie eine Liste numeric_features an, welche nur die Spaltennamen der numerischen Spalten enthält.
  4. Schreiben Sie die Namen aller nicht-numerischen Spalten in eine Liste categoric_features.
  5. Visualisieren Sie für die Spalten HST_Benennung, Neupreis Brutto, CO2-Emissionen und Produktgruppe die Verteilung der Werte in einem Barplot bzw. Histogramm.

Zur Ausgabe der numerischen Features filtern wir alle Spalten nach numerischem Type und speichern die Spaltennamen in eine Variable, die wir daraufhin ausgeben. Es wurden 12 numerische Features ausgegeben.

Zur Ausgabe der Kategorischen Features geben wir die Features aus, die nicht im Set der numerischen Features enthalten sind. Es sind 13 Kategorische Features in dem Dataframe enthalten. Dementsprechend wird es nötig sein, diese zu konvertieren, damit man sie auswerten kann.

Machine Learning 1: Produktgruppenbestimmung

In diesem Abschnitt soll ein Klassifikator trainiert werden, welcher anhand von Eingabemerkmalen, wie Breite, Höhe, Gewicht usw. das zugehörige Fahrzeugsegment (Produktgruppe) vorhersagt.

In diesem Teilversuch sollen als Eingabemerkmale die zuvor in numeric_features definierten Spalten und die nicht-numerischen Spalten Antrieb, Kraftstoffart, KSTA Motor verwendet werden. Die Zielvariable (Ausgabe) stellt die Spalte Produktgruppe dar.

Produktgrunppenspezifische Visualisierung

  1. Plotten Sie für die drei oben angegebenen nicht-numerischen Merkmale jeweils eine Produktgruppen-spezifische Häufigkeitsverteilung in der unten dargestellten Form.

  1. Plotten Sie für alle numerischen Merkmale jeweils einen Produktgruppen-spezifischen Boxplot in der unten dargestellten Form.

  1. Erzeugen Sie mit plotly.express scatter() einen 2-dimensionalen Plot, in dem alle Fahrzeuge wie folgt dargestellt werden (pro Fahrzeug ein Marker):

Zunächst werden die möglichen Werte angezeigt, die ein Zeileneintrag für die Spalte "Kraftstoffart" annehmen kann:

Diese Werte ergeben einzelne, übereinandergestapelte Balken mit der Anzahl der jeweiligen Kraftstoffarten als Höhe des Balkens für jede Produktgruppe. Definieren wird hierbei eine Funktion, die uns die relevanten Daten extrahiert und diese in einem horizontal Balken-Diagramm anzeigt.

Im Folgenden wird ein Diagramm geplotted, welches die Kraftstoffart pro Produktgruppe darstellt.

Man kann hier gut erkennen, dass Diesel und Benzin die Hauptkraftstoffarten sind. Des Weiteren fällt auf, dass es viele verschiedene Kraftstoffarten gibt, bei denen die meisten allerdings nur in sehr geringen Mengen vorhanden sind.

Im nächsten Diagramm wird der Antrieb der jeweiligen Produktgruppen dargestellt:

Auffaellig hier sind vor allem die kleineren Autos, welche fast ausschließlich Front-Antrieb haben. Allrad-Antrieb findet man vor allem in der Oberklasse und bei den Geländewägen.

Abschließend wird der Motortyp abhängig von der Produktgruppe geplotted:

Im Folgenden wird die 2. Darstellungform für numerische Datentypen benötigt.

Die Funktion kann genutzt werden, um beliebig viele der numerischen Merkmale zu plotten. Um das Anzeigen einfacher zu machen, kann die Variable "plots" anpasst werden, sodass nur die ersten x numerischen Merkmale geplotted werden. Um alle zu sehen, wird "plots" auf -1 gesetzt.

Jetzt wird versucht, ein interaktiven Plot mihilfe von Plotly zu erstellen. Dabei sollen die folgenden Bedingungen gelten:

Auch wenn der Plot nicht sonderlich übersichtlich ist, kann man gut erkennen, dass sich die verschiedenen Farben, also die Produktgruppen, in kleinen Clustern zusammenfinden. Das bedeuted, dass die Produktgruppen jeweils ähnliche Breiten und Längen haben.

Data Encoding

  1. Categoriale Merkmale ohne Ordnungsrelation (=nominale Merkmale) müssen One-Hot-Encodiert werden. Führen Sie für die drei categorialen Merkmale ein One-Hot-Encoding mit dem scikit-learn LabelBinarizer durch.
  2. Fügen Sie die one-hot-encodierten Spalten mit den numerischen Spalten zusammen. Weisen Sie die entsprechende Eingabedatenmatrix einem 2-dimensionalen numpy-array X zu.
  3. Führen Sie auf die Zielvariable Produktgruppe ein Label-Encoding mit scikit-learn LabelEncoder aus. Weisen Sie diese Daten dem 1-dimensionalen numpy-array y zu.

Im ersten Schritt werden die Numerischen und kategorische Features in eigene Dataframes extrahiert

Daraufhin werden die Werte in den Kategorischen Spalten kodiert und mit jedem durchlauf der For-Schleife jeweils mit dem Dataframe verknüpft.

Der y-Array wird nun mit Labelencoding kodiert. Daraufhin wird X in ein Numpy Array umgewandelt.

Generate Training- and Testpartition

Benutzen Sie die scikit-learn Methode train_test_split() um X und y in einer Trainings- und Testpartition aufzuteilen. 30% der Daten soll für das Testen, 70% für das Training benutzt werden.

Bei den numpy-Arrays der Eingabemerkmale wird mit "X" ein Großbuchstabe verwendet, was sich aus der mathematischen Konvention ableitet. X ist der Eingabewert in der Funktion f(x)=y und stellt einen mehrdimensionalen Array dar, während y einen eindimensionalen Array bzw. Vector darstellt.

Decision Tree Training, Test and Evaluation

  1. Trainieren Sie einen Entscheidungsbaum mit den Trainingsdaten.
  2. Wenden Sie den gelernten Entscheidungsbaum auf die Testdaten.
  3. Evaluieren Sie die Qualität des Entscheidungsbaumes indem Sie

    Interpretieren Sie das Ergebnis.

  4. Führen Sie eine 10-fache Kreuzvalidierung des Entscheidungsbaumes mit den Daten X und y aus. Interpretieren Sie das Ergebnis.

  5. Bestimmen Sie die Wichtigkeit der Eingabemerkmale für die Klassifikationsaufgabe, indem Sie auf den in 1.) gelernten DecisionTree das Attribut feature_importance_ abfragen. Stellen Sie die Werte in einem Barplot dar.

Die Genauigkeit auf dem Trainingsset beträgt 100%. Das bedeutet, dass der Tree für jeden Eintrag im Trainingsset das richtige Ergebnis erzeugt. Deshalb werden Abzweigungen auch für einzelne, vom Median stark abweichende Werte erzeugt. Das führt zu Overfitting. Höchstwahrscheinlich lässt sich die Genauigkeit auf dem Testset verbessern, indem wir mit Prepruning das Overfitting begrenzen.

Die erste Überlegung war, solange nach einer anderen maximalen Tiefe zu suchen, bis das Ergebnis nicht besser ist als das der vorherhigen Tiefe. Da dieses Vorgehen bedeuted hätte, erst einmal nur nach einem lokalen Maximum der Accuracy zu suchen, wurde die Idee jedoch verworfen.

Stattdessen wird eine maximal Tiefe definiert, innerhalb welcher nach dem Maximum der Accuracy geschaut wird. Dazu kann man eine Funktion verwenden, welche einen Classifier als input bekommt. Mit einer For-Schleife werden Decision Trees mit zunehmender Tiefe erstellt. Der Tree mit der höchsten Genauigkeit wird ausgegeben. Max_Depth beschreibt, bis welcher Tiefe getestet wird. Natürlich könnte ein globales Maximum auch mit einer größeren Tiefe existieren, jedoch ist es in diesem Fall äußerst unwahrscheinlich, da das lokale Maximum der Tiefe bereits bei 12 erreicht wird und wir davon ausgehen, dass overfitting mit steigender Tiefe eher zunimmt. Daher entschieden wir uns aus Effizienzgründen dafür die Maximale Tiefe auf 12 zu beschränken.

Nun wird die Methode find_best_classifier aufgerufen und der DecisionTreeClassifier als input übergeben.

Die optimale Tiefe beträgt 12. Die Genauigkeit ist nur leicht besser als ohne Beschränkung, es kommt also immernoch zu overfitting.

Mit default max_values = none ist max_values = n_values. Dementsprechend wird vermutlich keine oder eine nur geringe Randomisierung durch die Änderung des random_state auftreten.

Im nächsten Schritt erzeugen wir einen Classification Report. Dazu erstellen wir eine Funktion, welche als Input Parameter die Testpartitionen und den besten Decision Tree bekommt. In der Funktion werden einmal die Vorraussagen des Models in y_predicted gespeichtert. Daraufhin wird mit den Vorraussagen und den tatsächlichen Werten ein Classification Report erzeugt.

Beobachtungen

Die Präzesions und Recall Werte haben zwischen den unterschliedlichen Klassifizierungen relativ hohe DIfferenzen. Recall und Präzesionswerte in der selben Klasse liegen meistens nah beinander. Bei den beiden Differenzierungen mit dem größten Suppert, bzw. der größten Anzahl der Einträge in der Partition sind die Werte Überdurchschnittlich. Die Klassen Kompakt-SUV und Kompaktvan haben bei relativ vielen Einträgen niedrige Werte und wirken sich deshalb schlecht auf den Durchschnitt aus.

Bei Werten mit niedrigem Support ist es wahrscheinlicher, dass der Wert bei weiteren Testdaten von dem hier berechneten Wert abweicht.

Im Folgenden wird angenommen, dass Klassifizierungen, die sich in den am stärksten gewicheten Kategorien wenig von den anderen Klassifizierungen unterscheiden, oft miteinander verwechselt werden und daher niedrigere Präzesions und Callback Werte haben.

Im nächsten Schritt wird die Confusion Matrix ausgegeben.

Wie man sieht, befinden sich in der Diagonalen durchschnittlich hohe Werte. Bei einigen Klassifikationen befinden sich jedoch auch relativ hohe Werte bei anderen Labeln, was bedeutet, dass unser Modell in bestimmten Fällen falsche Vorrausagen macht. Bei wenigen Klassifikationen wird öfters falsch Vorrausgesagt als richtig. Das bedeutet, dass das Modell gerade in diesen Fällen nicht verlässlich ist. Es besteht die Vermutung, dass dies mit der inherenten Tendenz des Decision Trees zusammenhängt, auch bei begrenzter Tiefe noch zu Overfitting zu neigen. Häufig wurden Kompaktvans mit Geländewagen verwechselt.

Cross Validation:

Die Ergebnisse sind konsistent und einigermaßen gut, aber die Decision Trees haben tendenziell Probleme mit der Generalisierung (wegen Overfitting). Die Varianz ist relativ gering, jedoch besteht die Annahme, dass mit einem Modell mit besserer Generalisierung eine höhere Genauigkeit möglich ist.

Im nächsten Schritt wird die Gewichtung der Features in einem Diagramm ausgegeben:

Es ist zu beobachten, dass von den Kategorischen Features lediglich der Antrieb ('HA', 'FA', 'A') signifikant gewichtet ist. Interessant ist, dass die Fahzeugmaße "Länge", "Gesamtgewicht", "Höhe" und "Türen" am stärksten gewichtet wurden. Dies könnte zu Verwechslungen bei Fahrzeugtypen geführt haben, die sich in diesen Werten ähneln.

Random Forest Training, Test and Evaluation

Wiederholen Sie die Teilaufgaben 1. bis 5. des Entscheidungsbaums für einen Random Forest. Vergelichen Sie die Performance der beiden Verfahren.

Um den Random Forest zu erstellen wird erneut die "find_best_classifier" Methode genutzt, um pre_pruning über die Länge durchzuführen. Aufgrund der Tatsache, dass Random Forests deutlich generalisierbarer sind als Decision Trees, besteht die Annahme, dass die gewählte Tiefe und die Genauigkeit deutlich höhert sein werden als bei dem normalen Decision Tree.

Die verwendete Länge ist 26. Der Random Forest ist deutlich genauer als der vorher verwendete Decision Tree. Damit haben sich unsere Annahmen bestätigt.

Classification Report.

Insgesamt fällt auf, dass bei Klassifikationen mit Hoher Anzahl insgesamt hohe Genauigkeit auftritt. Bei Klassifikationen mit niedrigerer Anzahl ist dies nicht unbedingt der Fall, die Werte Schwanken je nach Produktgruppe. Gerade bei der Klasse "Minivan" ist das Modell ungenau, bei Pickup ist die Genauigkeit sehr hoch (wir gehen davon aus, dass dies daran liegt, dass Pick Ups relativ auffällige Fahrzeugmaße wie Höhe, Länge und Gewicht haben). Insgesamt funktioniert das Modell sehr gut, auffällig ist jedoch, dass oft die Genauigkeit mit der auffälligkeit von den Fahrzeugmaßen zusammenhängt (siehe Gewichtungen).

Confusion Matrix:

Insgesamt fällt auf, dass die Confusion Matrix genauer ausfällt als im vorherigen Beispiel. Einzelne Fälle sind nach wie vor nicht optimal. Einige Fälle bei denen es davor zu Ungenauigkeit kam wurden aber behoben. Hauptsächlich treten Probleme wegen Verwechslungen bei Coupes auf. Zwischen diesen Fahrzeugtypen gibt es große Gemeinsamkeiten, vor allem was Fahrzeugmaße und Gewicht betrifft.

DIe Werte sind insgesamt besser und sehr konsistent. Die Annahme, dass Random Forests in dieser Aufgabe zu einer besseren Generalisierung führen, hat sich bestätigt.

Classification Report:

Es besteht die Annahme, dass die einzelnen Features sehr ähnlich gewichtet werden, wie bei dem Decision Tree.

Grundsätzlich ist die Gewichtung ähnlich, auffällig ist aber, dass die Gewichtungen gleichmäßiger auf die Features verteilt werden. Auch kategorische Features werden mehr mit einbezogen. Dies könnte damit zusammenhängen, dass das Model das Overfitting reduziert.

Machine Learning 2: Schätzung der CO2-Emission

In diesem Teilversuch soll aus den Eingabemerkmalen

"CCM","HST PS", "Anzahl der Türen", "Leergewicht", "Zuladung", "Länge", "Breite", "Höhe"

die Zielvariable

CO2-Emissionen

geschätzt werden. Hierzu soll ein möglichst gutes Regressionsmodell trainiert werden.

Visuelle Korrelationsanalyse

  1. Stellen Sie für jedes der 8 Eingabemerkmale die Korrelation mit der Zielvariablen visuell in einem Scatterplot dar, in dem das jeweilige Eingabemerkmal auf der x-Achse und die Zielvariable auf der y-Achse aufgetragen wird.
  2. Diskutieren Sie die Korrelationen. Welche Merkmale korrelieren am stärksten mit der Zielvariable? Erscheint Ihnen das plausibel?

Die Scatterblocks werden erstellt und angezeigt. Die Y-Achse ist jeweils die Zielvariable CO2-Emissionen.

Diskussion der Scatterblocks:

Die Features CCM (Hubraum in Kubikzentimetern), HST PS, Länge und Breite und Höhe und Leergewicht korrelieren deutlich erkennbar mit der Zielvariablen CO2-Emissionen. Dies erscheint plausibel, denn es ist davon auszugehen, dass diese Merkmale in der Tendenz jeweils proportional mit der Zielvariablen sind, d.h. je größer, schwerer und leistungsstärker ein Auto, desto größer sein CO2-Ausstoß. Gleichzeitig ist zu vermuten, dass es davon Ausnahmen gibt.

Der Scatterblock mit dem Merkmal Anzahl der Türen zeigt hingegen, dass es für jede gegebene Anzahl von Türen jeweils eine große Diversität an verschiedenen CO2-Ausstoß-Werten gibt. Dies ist einfach zu erklären: Der zweitürige, leistungsstarke Porsche Cabrio hat mehr CO2-Ausstoß als beispielsweise ein ebenso zweitüriger Smart. Und ein leistunsstarker 4-türiger SUV hat vermutlich mehr CO2-Ausstoß als ein ebenso 4-türiger VW Polo.

Das Merkmal Zuladung scheit kaum mit der Zielvariablen zu korrilieren. Eine schwerere Zuladung scheint bei einem Auto also keinen maßgeblich größeren CO2-Ausstoß nach sich zu ziehen. Dies könnte insofern plausibel sein, dass der CO2-Ausstoß eines Autos von der Leistungsstärke (HST PS und CCM) des Motors abhängen dürfte, und ein Motor bei moderater Zuladung (die sowieso durch das Ladevolumen eines Autos begrenzt ist) nicht plötzlich anders funktioniert als ohne Zuladung. Bei gleicher Geschwindigkeit und größerer Zuladung müsste der CO2-Ausstoß trotzdem ansteigen, deswegen wäre es überraschend, wenn gar kein Zusammenhang vorliegt.

Data Encoding

  1. Weisen Sie die Matrix der Eingabedaten dem 2-dimensionalen Array X und die Zielvariable dem 1-dimensionalen Array y zu.
  2. Führen Sie auf X und y eine Partitionierung in Trainings- und Testdaten durch, wieder im Verhältnis 70/30.
  3. Skalieren Sie die Eingabevariablen und die Zielvariable mit dem MinMaxScaler. Die Skalierung muss sowohl auf Trainings- als auch auf Testdaten ausgeführt werden. Warum darf die Skalierung erst nach dem Split in die beiden Partitionen ausgeführt werden? Worauf ist zu achten?

Die Eingabedaten werden dem 2-dimensionalen numpy-Array X zugewiesen:

Die Zielvariable wird dem 1-dimensionalen numpy-Array y zugewiesen:

Die Arrays X und y werden in Trainings- und Testdaten (Verhältnis 70/30) partioniert:

Es ist wichtig, den MinMaxScaler erst nach Erstellung der Partitionen anzuwenden und fit() auf die Trainingsdaten auszuführen. Das liegt daran, dass ansonsten Werte aus den Testdaten das Scaling und damit das Trainingsmodell beeinflussen könnten. Derartige Abweichungen sollten vermieden werden. Desweiteren muss man darauf achten, die Dimensionalität des Arrays mit Shape() und Flatten() and den Scaler und die weiteren Schritte anzupassen.

Training, Test und Evaluation verschiedener Regressionsmodelle

Führen Sie die folgenden Teilaufgaben sowohl für ein Single Layer Perceptron als auch für ein Multi Layer Perceptron mit 20 Neuronen in der Hidden-Schicht durch. Vergleichen Sie am Ende die Performance der beiden Verfahren.

  1. Trainieren Sie den Algorithmus mit den Trainingsdaten.
  2. Wenden Sie das gelernte Modell auf die Testdaten an.
  3. Evaluieren Sie die Qualität der Modelle, indem Sie auf die vorhergesagten Ausgaben und die wahren Ausgaben die unten gegebene Funktion aufrufen.
  4. Beschreiben Sie kurz die in der Funktion verwendeten Metriken
Single Layer Perceptron

Der Algorithmus wird mit den Trainingsdaten trainiert:

Zuerst wird die vorgegebene Funktion zum Anzeigen der Performance-Metriken definiert.

Jetzt wird eine Pipeline inklusive eines Skalierung und eines SLP erstellt. Die Skalierung ist wichtig, damit die Daten so "normalisiert" werden, dass sie optimal von SLP verarbeitet werden koennen.

z = (x - u) / s

Z ist dabei der skalierte Wert, x der zu skalierende Wert, u der Mittelwert und s die Standartabweichung.

Jetzt wird die Pipeline mit den Trainingsdaten trainiert.

Nun werden einige Performance-Metriken dieser Pipeline berechnet und ausgegeben.

Da SLPs sind in der Regel bei einfachen Problemstellungen effektiv, also bei Eingabedaten, die eine lineare Loesungsfunktion haben. Da die Daten, wie in den vorhergehenden Plots zu sehen ist, durchaus lineare Abhaengigkeiten haben, wird davon ausgegagen, dass auch mit einem SLP eine durchaus gute Performance herauskommen kann.

Wie am Ergebnis zu sehen, ist die Performance nicht sonderlich gut. Dies ist wahrhscheinlich darauf zurueckzufuehren, dass ein SLP nicht ausreicht, um eine Kurve darzustellen, die in manchen Faellen das Problem effektiver loesen koennte als eine Gerade.

Ein MLP kann diesem Problem besser begegnen. Deshalb wird im naechsten Schritt ein Multi Layer Perceptron erstellt, welches die Aufgabe der CO2-Emissionsvorhersage mit hoehrerer Wahrscheinlichkeit loesen soll. Als Parameter wird ein random_state mitgegeben, der die Reproduzierbarkeit garantiert, und die Konfiguration der versteckten Schicht, welche in diesem Fall eine Schicht mit 20 vollvernetzen Neuronen darstellt.

Nun wird das MLP mit den Trainingsdaten trainiert und ein Score mithilfe der Testdaten ausgegeben. Zusaetzlich dazu werden einige Fehlerraten mit der vorgegebenen Funktion determineRegressionMetrics ausgegeben.

Wie erwartet sind die Ergebnisse des MLP besser, jedoch ist der Unterschied marginal.

Daraus laesst sich schliessen, dass hauptsaechlich fast lineare Abhaengigkeiten zwischen den Daten vorhanden sind. Ob der Einsatz eines MLP mehr Sinn ergibt haengt nun von der Anwendung ab, und in welchem Kontext die Berechnungen durchgefuehrt werden.

Hyperparameteroptimierung

Für ein Multi Layer Perceptron soll eine Hyperparameteroptimierung durchgeführt werden. Ziel ist es innerhalb der unten vorgegebenen Wertebereiche für die Hyperparameter hidden_layer_sizes, activation und learning_rate die beste Konfiguration zu finden. Hierzu kann entweder GridSearchCV oder RandomizedSearchCV eingesetzt werden. GridSearchCV testet einfach alle Konfigurationen durch, benötigt daher aber viel Zeit. RandomizedSearchCV geht heuristisch und damit schneller durch den Suchraum. Wenden Sie eines dieser beiden Verfahren an, um für das unten gegebene Parameter-Grid die optimale Konfiguration zu finden. Welches ist die optimale Konfiguration und zu welchem neg_mean_absolute_error führt diese?

Es wird für jeden Hyperparameter eine endliche Menge an Werten festgelegt, da die Suche nach der besten Konfiguration sonst zu lange bzw. bei reelen, also unbegrenztengan Hyperparameterräumen unendlich lange dauern würde.

Jetzt wird mit Hilfe der zwei Hyperparameter-Suchmethoden herausgefunden, welche Hyperparameter die besten für die gegebene Domaene ist. Um das Suchen zu generalisieren, wird eine Methode erstellt, die gegeben einer Suchmethode nach dem MLP mit dem besten Score sucht.

Nun wird diese Methode mit der ersten Suchmethode, dem GridSearchCV, ausgefuehrt. Dies kann, je nach Leistungsfaehigkeit des eigenen Rechners durchaus etwas laenger dauern, da die GridSearchCV-Methode alle gegebenen Parameterkonfigurationen ausprobiert, um die Beste zu finden.

Da alle Konfigurationen ueberprueft werden, kann davon ausgegangen werden, dass auch wirklich die beste Konfiguration gefunden wird.

Die Suchfunktion Grid-Search gibt 50 als besten Wert für die Hidden-Layer zurück. Bei dieser großen Zahl ist es gut verständlich, dass gleichzeitg Relu als beste Aktivierungsfunktion ausgemacht wurde, da sie die beliebteste, weil geeignetste Funktion für Deep Learning ist. Dies liegt neben ihrer größeren Schnelligkeit und geringerer Operationskosten im Vergleich zu der Sigmoid- und der Tanh-Funktion an dem ''Verschwindenden-Gradienten''-Problem der letztgenannten Funktionen. Für sehr große und sehr kleine Eingabewerte saturiert die Sigmoid-Funktion bei 1 bzw. 0, die Tanh-Funktion bei 1 bzw. -1, was an diesen Stellen extrem kleine Ableitungen zur Folge hat. Somit können aus tieferen Schichten nur sehr kleine Gradienten als Informationen durch das neuronale Netz gesendet werden. Dies führt zu weniger effektivem Lernen im Vergleich zur Relu-Funktion, die dieses Problem nicht mit sich bringt.

Nun wird sich der zweiten Suchmethode, der RandomizedSearchCV, zugewandt. Anders als der Grid-Search, werden hier nicht alle Konfigurationen ausprobiert, sondern nur zufaellig ausgewaehlte Parameterkonfigurationen.

Dementsprechend kann man hier nicht davon ausgehen, dass das optimale Ergebnis gefunden wird. Der Vorteil ist dabei, dass nicht alle Konfigurationen ausprobiert werden, was, vor allem bei leistungsschwacherer Hardware einen enormen Zeitvorteil bringt. Dies kann auch genauer mithilfe des n_iter-Paramteres genauer spezifiziert werden, was allerdings fuer diesen Versuch nicht notwendig ist, da es nicht zu viele Parameterkonfigurationen gibt.

Wie erwartet, kommt hierbei ein anderes, weniger optimales Ergebnis heraus. Der Prozess hat ebenfalls wie erwartet durchaus kuerzer gedauert, da nicht alle Konfigurationen ausprobiert werden.

Da der Grid-Search immer das optimale Ergebnis liefert, wird sich in diesem Falle auch auf das Ergebenis dieser Methode verlassen, und das Ergebnis des Randomized-Searches lediglich fuer Demonstrationszwecke verwendet.